package org.gitee.lz.xspring.utils;

import java.lang.reflect.Type;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.google.protobuf.util.JsonFormat;
import lombok.extern.slf4j.Slf4j;

/**.
 * @ClassName: ProtoUtil
 * @Description: ProtoUtil, proto工具类
 * @Author: lizhen
 * @Date: 2024-01-03 11:29
 * @Version: 1.0
 **/
@Slf4j
public final class ProtoUtil {

  private static final Gson gson = new GsonBuilder().create();

  private ProtoUtil() {
  }

  /**
   * convert proto message to pojo bean.
   *
   * @param clazz         pojo class.
   * @param sourceMessage proto message.
   * @param <T>           type param.
   * @return pojo object.
   */
  public static <T> T toPojoBean(Class<T> clazz, Message sourceMessage)
      throws InvalidProtocolBufferException {
    if (clazz == null) {
      throw new IllegalArgumentException("No destination pojo class specified");
    }
    final String json = protoToString(sourceMessage);
    return gson.fromJson(json, clazz);
  }

  /**
   * convert pojo object to message builder.
   *
   * @param destBuilder    message builder.
   * @param sourcePojoBean pojo object.
   */
  public static void toProtoBean(Message.Builder destBuilder, Object sourcePojoBean)
      throws InvalidProtocolBufferException {
    if (destBuilder == null) {
      throw new IllegalArgumentException("No destination message builder specified");
    }
    if (sourcePojoBean == null) {
      throw new IllegalArgumentException("No source pojo specified");
    }
    toProtoBuilder(gson.toJson(sourcePojoBean), destBuilder);
  }

  /**
   * deserialize proto string.
   */
  public static void toProtoBuilder(String json, Message.Builder destBuilder)
      throws InvalidProtocolBufferException {
    if (json == null || json.isEmpty()) {
      return;
    }

    try {
      JsonFormat.parser().ignoringUnknownFields().merge(json, destBuilder);
    } catch (InvalidProtocolBufferException e) {
      log.error("deserialize proto string failed:\n" + json, e);
      throw new InvalidProtocolBufferException("deserialize proto string failed");
    }
  }

  /**
   * protobuf to string.
   */
  public static String protoToString(Message message) throws InvalidProtocolBufferException {
    try {
      return JsonFormat.printer().print(message);
    } catch (InvalidProtocolBufferException e) {
      log.error("serialize proto failed", e);
      log.error("proto message:\n{}", message);
      throw new InvalidProtocolBufferException("deserialize proto string failed");
    }
  }

  /**
   * convert object to string.
   *
   * @param object a pojo object.
   * @return string.
   */
  public static String toJson(Object object) {
    return gson.toJson(object);
  }

  /**
   * convert to a pojo array from a json string.
   *
   * @param clazz pojo class.
   * @param json  json input.
   * @param <T>   type param.
   * @return a pojo array.
   */
  public static <T> T[] toArray(Class<T[]> clazz, String json) {
    return gson.fromJson(json, clazz);
  }

  /**
   * json string to pojo.
   */
  public static <T> T fromJson(String json, Class<T> clazz) {
    return gson.fromJson(json, clazz);
  }

  /**
   * json string to pojo.
   */
  public static <T> T fromJson(String json, Type typeOfT) {
    return gson.fromJson(json, typeOfT);
  }
}
